package com.woniuxy.yoga.admin.permission;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniuxy.yoga.admin.dao.mapper.AdminAccountMapper;
import com.woniuxy.yoga.admin.dao.mapper.AdminRoleMapper;
import com.woniuxy.yoga.admin.dao.mapper.ResourceMapper;
import com.woniuxy.yoga.admin.dao.mapper.RoleResourceMapper;
import com.woniuxy.yoga.admin.dao.model.AdminAccount;
import com.woniuxy.yoga.admin.dao.model.AdminRole;
import com.woniuxy.yoga.admin.dao.model.Resource;
import com.woniuxy.yoga.admin.dao.model.RoleResource;
import com.woniuxy.yoga.admin.model.exception.AdminException;
import com.woniuxy.yoga.commons.core.statedata.AdminState;
import com.woniuxy.yoga.commons.core.util.JWTUtil;
import com.woniuxy.yoga.commons.redis.util.RedisUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Aspect
public class PermissionAop {
    @Autowired
    private AdminRoleMapper adminRoleMapper;
    @Autowired
    private RoleResourceMapper roleResourceMapper;
    @Autowired
    private ResourceMapper resourceMapper;
    @Autowired
    private AdminAccountMapper adminAccountMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Pointcut("@annotation(com.woniuxy.yoga.admin.permission.Permission)")
    private void pt(){}

    //使用前置通知时，使用的是JoinPoint类型的对象，该对象直接去获取该方法的类的对象去newInstance产生类
    //我们需要将其改为环绕通知
    @Around("pt()")
    public Object permissionController(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("---------------进入管理员动态权限校验-------------------");
        //先校验管理员是否合法
        //从jwt令牌中获取当前登录管理员的账号id
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String adminToken = request.getHeader("authen_token");
        String refreshToken = redisUtil.get(adminToken);
        //获取登录管理员的ID
        Integer accountId = JWTUtil.getAdminId(refreshToken);
        //判断令牌是否存在，如果存在，做下一步操作，如果不存在，没有登录，抛出异常
        if(!StringUtils.hasLength(refreshToken)) throw new AdminException(401,"登录未认证");

        //根据账号id查询当前管理员拥有的角色
        AdminAccount adminAccount = adminAccountMapper.selectById(accountId);
        AdminRole adminRole = adminRoleMapper.selectById(adminAccount.getRoleId());
        //查权限
        LambdaQueryWrapper<RoleResource> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RoleResource::getRoleId,adminRole.getRoleId()).
                eq(RoleResource::getRelationState, AdminState.ROLE_RELATION_STATE_RIGHT);
        List<RoleResource> roleResources = roleResourceMapper.selectList(queryWrapper);
        List<Integer> resourceIds = roleResources.stream().map(o -> o.getResourceId()).collect(Collectors.toList());
        List<Resource> resources = new ArrayList<>();
        if (resourceIds!=null&&resourceIds.size()>0) {
             resources = resourceMapper.selectBatchIds(resourceIds);
        }
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        Object[] args = pjp.getArgs();
        //如果切入点方法上存在Permission注解，表示访问该方法需要进行权限控制
        boolean flag = false;
        Object value = null;
        if (method.isAnnotationPresent(Permission.class)) {
            Permission permission = method.getAnnotation(Permission.class);
            String[] resourceNames = permission.resources();
            //默认情况，只需认证，不需要授权
            if (resourceNames.length==0) {
                value = pjp.proceed(args);
            }else {
                //判断权限
                for (String resourceName : resourceNames) {
                    for (Resource resource : resources) {
                        if (resource.getResourcePath().equals(resourceName)) {
                            flag=true;
                            break;
                        }
                    }
                    if (flag) {
                        break;
                    }
                }

                if (flag) {
                    value = pjp.proceed(args);
                }else {
                    throw new AdminException(403,"没有权限");
                }
            }
        }
        return value;
    }

}
